package com.wky.sensitive.util;
import com.wky.sensitive.annotation.ChildReplace;
import com.wky.sensitive.annotation.Replace;
import com.wky.sensitive.enums.DataTypeEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 替换工具类
 *
 * @author weikaiyu
 * @version 1.0
 * @date 2022-02-11 10:46
 */
public class ReplaceUtils {

    private static Logger logger = LoggerFactory.getLogger(ReplaceUtils.class);

    /**
     * 将字符串开始位置到结束位置之间的字符用指定字符替换
     *
     * @param sourceStr   待处理字符串
     * @param begin       开始位置
     * @param end         结束位置
     * @param replacement 替换字符
     * @return
     */
    public static String replaceBetween(String sourceStr, int begin, int end, String replacement) {
        if (sourceStr == null) {
            return "";
        }
        if (replacement == null) {
            replacement = "*";
        }
        int replaceLength = end - begin;
        if (org.apache.commons.lang3.StringUtils.isNotBlank(sourceStr) && replaceLength > 0) {
            StringBuilder sb = new StringBuilder(sourceStr);
            sb.replace(begin, end, org.apache.commons.lang3.StringUtils.repeat(replacement, replaceLength));
            return sb.toString();
        } else {
            return sourceStr;
        }
    }


    /**
     * 身份证前三后四脱敏
     *
     * @param id
     * @return
     */
    public static String idEncrypt(String id) {
        if (StringUtils.isEmpty(id) || (id.length() < 11)) {
            return id;
        }
        return id.replaceAll("(?<=\\w{3})\\w(?=\\w{4})", "*");
    }


    /**
     * 银行卡脱敏
     *
     * @param card
     * @return
     */
    public static String idEncrypts(String card) {

        return replaceBetween(card, 3, card.length() - 4, null);
    }


    /**
     * 获取实体类上的所有@Replace注解
     *
     * @param obj 要获取@Replace的实体类
     * @return 标识有该注解的属性集合  key为实体属性字段，value为规则路径rulePath
     */
    public static Map<String, Object> getReplaceInfo(Object obj) {
        if (null == obj) {
            return new HashMap<String, Object>();
        }
        Map<String, Object> map = new HashMap<String, Object>();
        getReplaceInfo(obj, map);
        return map;
    }

    /**
     * 递归获取实体类上的所有@Replace注解
     *
     * @param obj 要获取@Replace的实体类
     * @param map map集合
     */

    private static void getReplaceInfo(Object obj, Map<String, Object> map) {
        Field[] declaredFields = obj.getClass().getDeclaredFields();
        for (Field field : declaredFields) {
            String name = field.getName();
            field.setAccessible(true);
            // 如果属性是String
            if (field.getGenericType().toString().equals(
                    "class java.lang.String")) {
                // 获取注解
                Replace replace = field.getAnnotation(Replace.class);
                // 获取属性值,put进map
                setKeyAndRuleClass(replace, map, name);
                continue;
            }
            //如果属性含有 ChildReplace注解
            ChildReplace childReplace = field.getAnnotation(ChildReplace.class);
            // 存在注解
            if (null != childReplace) {
                // 将属性名称put进map，对字段脱敏时，可以找到该字段进行递归
                map.put(name, childReplace.rulePath());
                // 如果是集合
                if (childReplace.dataType() == DataTypeEnum.LIST) {
                    // 获取数据
                    try {
                        List<Object> objectList = (List<Object>) field.get(obj);
                        if (null != objectList && objectList.size() >= 1) {
                            // 递归获取注解
                            getReplaceInfo(objectList.get(0), map);
                        }
                    } catch (IllegalAccessException e) {
                        logger.error("获取属性{}失败，异常：", name, e.getMessage());
                    }
                }
                // 如果是实体
                if (childReplace.dataType() == DataTypeEnum.ENTITY) {
                    // 递归获取注解
                    Object vo = null;
                    try {
                        vo = field.get(obj);
                    } catch (IllegalAccessException e) {
                        logger.error("获取属性{}失败，异常：", name, e.getMessage());
                    }
                    if (null != vo) {
                        getReplaceInfo(vo, map);
                    }

                }
            }

        }
    }

    /**
     * 根据属性名称，获取数据
     *
     * @param FieldName 属性名称
     * @param clazz     反射调用的对象
     * @return
     */
    public static Object getMethodData(String FieldName, Class clazz) {
        Method method = null;
        try {
            method = clazz.getDeclaredMethod("get" + getMethodName(FieldName));
            method.setAccessible(true);
        } catch (NoSuchMethodException e) {
            logger.error("属性{}没有添加get方法,异常：{}", FieldName, e.getMessage());
            e.printStackTrace();
        }
        try {
            return method.invoke(clazz.newInstance());
        } catch (IllegalAccessException e) {
            logger.error(e.getMessage());
        } catch (InvocationTargetException e) {
            logger.error(e.getMessage());
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 获取属性值,put进map
     *
     * @param replace
     * @param map
     */
    private static void setKeyAndRuleClass(Replace replace, Map<String, Object> map, String key) {
        if (replace != null) {
            // 获取替换规则
            Object rulePath = replace.rulePath();
            // 存储健值与规则
            map.put(key, rulePath);
        }
    }


    /**
     * 把一个字符串的第一个字母大写、效率是最高的、
     *
     * @param fildeName 属性名称
     * @return get +属性名称
     */
    public static String getMethodName(String fildeName) {
        byte[] items = fildeName.getBytes();
        items[0] = (byte) ((char) items[0] - 'a' + 'A');
        return new String(items);
    }
}

